﻿global using Devonline.Core;
global using Devonline.Entity;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Mapster;
using Microsoft.Extensions.Primitives;

namespace Devonline.Abstractions;

/// <summary>
/// 公共数据业务处理服务
/// </summary>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public interface IDataService<TEntitySet, TKey>
    where TEntitySet : class, IEntitySet<TKey>
    where TKey : IConvertible
{
    #region 基础属性和方法, 业务无关属性和方法
    /// <summary>
    /// 当前登录的用户编号
    /// </summary>
    TKey UserId { get; }
    /// <summary>
    /// 当前登录用户的所属组织单位编号
    /// </summary>
    TKey? GroupId { get; }
    /// <summary>
    /// 数据隔离的数据编号
    /// </summary>
    TKey? DataIsolateId { get; }
    /// <summary>
    /// 当前登录的用户名
    /// </summary>
    string UserName { get; }
    /// <summary>
    /// 当前处理的数据对象类型名称
    /// </summary>
    string TypeName { get; }
    /// <summary>
    /// 当前对象是否启用了缓存
    /// </summary>
    bool EnableCache { get; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// 用户尚未登录系统时抛出 UnauthorizedAccessException 异常
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UnauthorizedAccessException">用户尚未认证访问异常</exception>
    TKey? GetUserId();
    /// <summary>
    /// 从 httpContext 获取用户主要组织单位标识, 用户标识在 User.Claims 中的 type 为 groupId; 在 request 中为 groupId
    /// 用户尚未登录系统时抛出 UnauthorizedAccessException 异常
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UnauthorizedAccessException">用户尚未认证访问异常</exception>
    TKey? GetGroupId();
    /// <summary>
    /// 获取数据隔离的数据编号
    /// </summary>
    /// <returns></returns>
    TKey? GetDataIsolateId();
    /// <summary>
    /// 从 httpContext 获取用户名, 用户名在 User.Claims 中的 type 为 UserName;
    /// </summary>
    /// <returns></returns>
    string GetUserName();
    /// <summary>
    /// 从当前 request 中获取参数的值, 取值顺序 Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    T? GetRequestOption<T>(string key) where T : IConvertible;
    /// <summary>
    /// 获取上下文对象中参数/变量的值, 取值顺序 HttpContext -> Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    T? GetContextOption<T>(string key) where T : IConvertible;
    /// <summary>
    /// 获取查询选项列表
    /// </summary>
    /// <returns>返回合并后的查询选项</returns>
    IEnumerable<KeyValuePair<string, StringValues>> GetRequestOptions();
    /// <summary>
    /// 从分布式缓存获取值
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns></returns>
    Task<TValue?> GetCacheAsync<TValue>(string key);
    /// <summary>
    /// 将值写入分布式缓存, 带预设过期时间
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    Task SetCacheAsync<TValue>(string key, [DisallowNull] TValue value);
    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <returns></returns>
    Task RefreshCacheAsync();
    /// <summary>
    /// 设置当前数据隔离的编号
    /// </summary>
    /// <param name="dataIsolateId">数据隔离编号</param>
    void SetDataIsolateId(TKey? dataIsolateId = default);
    #endregion

    #region 基础数据操作方法, 面向当前业务类型
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <returns></returns>
    TypeAdapterConfig GetAdapterConfig();
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet"></param>
    void Create(TEntitySet entitySet);
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet"></param>
    void Update(TEntitySet entitySet);
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    void Update(TEntitySet entitySet, bool isLogical = false);
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    void Delete(TEntitySet entitySet, bool isLogical = false);
    #endregion

    #region 数据查询方法, 面向当前业务类型
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <returns></returns>
    IQueryable<TEntitySet> GetQueryable();
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    IQueryable<TEntitySet> GetQueryable(Expression<Func<TEntitySet, bool>> predicate);
    /// <summary>
    /// 根据条件查询第一条
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    Task<TEntitySet?> FirstOrDefaultAsync(Expression<Func<TEntitySet, bool>> predicate);
    /// <summary>
    /// 根据条件查询是否存在
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    Task<bool> AnyAsync(Expression<Func<TEntitySet, bool>> predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    Task<TEntitySet?> GetAsync(TKey id);
    /// <summary>
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <returns></returns>
    Task<PagedResult<TEntitySet>> GetPagedResultAsync();
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task<TEntitySet> GetIfExistAsync(TKey id);
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task ThrowIfExistAsync(TKey id);
    /// <summary>
    /// 从缓存/数据库获取 TEntitySet 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <typeparam name="TResult">字段/属性类型</typeparam>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    Task<TResult> GetNextCodeAsync<TResult>(Expression<Func<TEntitySet, TResult>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = AppSettings.UNIT_ONE);
    /// <summary>
    /// 从缓存/数据库获取 TEntitySet 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    Task<string> GetNextCodeAsync(Expression<Func<TEntitySet, string>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = AppSettings.UNIT_ONE);
    #endregion

    #region 数据查询方法, 面向非当前业务类型, 仅适用于主业务数据关联查询的业务场景, 没有当前主业务数据处理的性能好 
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <returns></returns>
    IQueryable<T> GetQueryable<T>() where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    IQueryable<T> GetQueryable<T>(Expression<Func<T, bool>> predicate) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 根据条件查询第一条
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    Task<T?> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> predicate) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 根据条件查询是否存在
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    Task<bool> AnyAsync<T>(Expression<Func<T, bool>> predicate) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    Task<T?> GetAsync<T>(TKey id) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <returns></returns>
    Task<PagedResult<T>> GetPagedResultAsync<T>() where T : class, IEntitySet<TKey>, new();
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task<T> GetIfExistAsync<T>(TKey id) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task ThrowIfExistAsync<T>(TKey id) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 从缓存/数据库获取 TEntitySet 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <typeparam name="T">数据对象模型类型</typeparam>
    /// <typeparam name="TResult">字段/属性类型</typeparam>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    Task<TResult> GetNextCodeAsync<T, TResult>(Expression<Func<T, TResult>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = AppSettings.UNIT_ONE) where T : class, IEntitySet<TKey>;
    /// <summary>
    /// 从缓存/数据库获取 TEntitySet 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <typeparam name="T">数据对象模型类型</typeparam>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    Task<string> GetNextCodeAsync<T>(Expression<Func<T, string>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = AppSettings.UNIT_ONE) where T : class, IEntitySet<TKey>;
    #endregion

    #region 数据操作方法, 面向当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task UniqueAsync(TEntitySet entitySet);
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TEntitySet> AddAsync(TEntitySet entitySet, ServiceContext? context = default);
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TEntitySet> UpdateAsync(TEntitySet entitySet, ServiceContext? context = default);
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task DeleteAsync(TKey id, ServiceContext? context = default);
    #endregion

    #region 批量数据操作方法, 面向当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task UniquesAsync(IEnumerable<TEntitySet> entitySets);
    /// <summary>
    /// 数据对象的新增, 自动验证重复
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task AddsAsync(IEnumerable<TEntitySet> entitySets, ServiceContext? context = default);
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task UpdatesAsync(IEnumerable<TEntitySet> entitySets, ServiceContext? context = default);
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="ids">业务数据主键集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task DeletesAsync(IEnumerable<TKey> ids, ServiceContext? context = default);
    #endregion

    #region 数据操作方法, 面向当前业务类型的视图模型
    /// <summary>
    /// 数据对象的新增, 自动验证重复
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModel">业务数据视图模型</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TEntitySet> AddAsync<TViewModel>(TViewModel viewModel, ServiceContext? context = default) where TViewModel : class, IViewModel<TKey>;
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModel">业务数据视图模型</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TEntitySet> UpdateAsync<TViewModel>(TViewModel viewModel, ServiceContext? context = default) where TViewModel : class, IViewModel<TKey>;
    #endregion

    #region 批量数据操作方法, 面向当前业务类型的视图模型
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModels">业务数据视图模型集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task AddsAsync<TViewModel>(IEnumerable<TViewModel> viewModels, ServiceContext? context = default) where TViewModel : class, IViewModel<TKey>;
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModels">业务数据视图模型集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task UpdatesAsync<TViewModel>(IEnumerable<TViewModel> viewModels, ServiceContext? context = default) where TViewModel : class, IViewModel<TKey>;
    #endregion
}

/// <summary>
/// 字符串作为主键的默认实现接口
/// </summary>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
public interface IDataService<TEntitySet> : IDataService<TEntitySet, string> where TEntitySet : class, IEntitySet;